This page last changed on May 21, 2012 by brian.

Extracting Spots from ESP Images

The spots extracted from the image are used to calculate the fitness of the match between the GAL file and image file.

The Short Version

The first step is to read the raw tiff image:

import java.io.File
import java.net.URL
import org.mbari.esp.ia.services.ToolBox
// READ ESP TIFF IMAGE
val io = ToolBox.imageIOService
val image = io.read(new URL("file:/test08/pcr11jun2520h2000ml40s.tif")).getProcessor
// WRITE AS PNG 
io.write(image, new File("pcr11jun2520h2000ml40s.png"))

There is a small function call that extracts the image spots.

import org.mbari.esp.ia.imglib.MultiPassRegionsExtractorFn
val regions = MultiPassRegionsExtractorFn(image)
val points = regions.map(_.centroid).toSeq

The above code snippet does the following:

  1. Extracts the bright regions of the image that are most likely to represent spots of interest.
    • A region is just a collection of pixels that form a contiguous bright region
  2. regions.map(_.centroid).toSeq Converts each region to a point by calculating the centroid of the region. The collection of points is then converted to a Seq which is roughly equivalent to a java.util.List

We can draw the extracted points onto the image using this bit of code:

import java.io.File
import java.awt.{Color, Graphics2D}
import java.awt.geom.{Ellipse2D}
import java.awt.image.BufferedImage
import org.mbari.esp.ia.imglib.ToRGBFn

val colorImage = ToRGBFn(image).getBufferedImage
val g2 = colorImage.getGraphics.asInstanceOf[Graphics2D]
val circles = points.map(p =>
    new Ellipse2D.Double(p.x - 2, p.y - 2, 4, 4))
g2.setPaint(Color.GREEN)
circles.foreach(g2.draw(_))
io.write(colorImage, new File("pcr11jun2520h2000ml40ss-with-extracted-spots.png"))
g2.dispose()

Under the Hood

In order to extract the regions that represent the spots we threshold at multiple levels using 2 different image treatments. The 2 image treatments are:

  1. Raw (no treatment)
  2. Standard deviation of image using a local 3x3 kernel.

We calculate a base threshold for each treatment using maximum entropy. Then we make multiple passes across the image from using the following:

val bound = 25 // Add and subtract this from threshold to get range
val minVal = if (split - bound < 0) split else split - bound
val maxVal = if (split + bound > 255) split else split + bound
for (i <-  minVal to maxVal by 5) {
    // Process image. ie. apply threshold and floodfill
}

For each pass across an image, we aggregate all the regions into a collection. During the aggregations we eliminate spurious or duplicate regions by:

  1. Exclude non-round regions. That is, it excludes regions that have odd shapes or ellipses as they are not likely to be spots of interest (e.g. filter(_.eccentricity()._1 < 3))
  2. Exclude regions that contain less than 10 pixels.
  3. Exclude regions whose centroid duplicates an existing accepted region. (i.e. c1.x == c2.x && c1.y == c2.y)
  4. Drop regions whose centroid is within 3 pixels of another region. These are likely to be regions that represent the same spot.

Extracting Image Point Features 2


Document generated by Confluence on Feb 03, 2026 14:16